🔥 交易所注册优惠: 🔶 币安(Binance) 点击注册 (邀请码:SRXT5KUM/合约:416378774) ⚫ 欧易(OKX) 点击注册 (邀请码:A999998)

Gemini 帮我写了一个快速插入内容的油猴脚本

175次阅读
没有评论

GPT 的代码能力真不行

事情是这样的,我想常用“▸”这个特殊符号,但正常的输入法打出来太麻烦了

于是我灵机一动,让 GPT 写个油猴脚本设置快捷键输入

结果它改了好几次都不行,换成 Gemini 一次就成功了😅

真就 AI 比 AI,气死人

最后让 Gemini 写了一个完全版,可以自定义快捷键,还能插入 emoji 😄
Gemini 帮我写了一个快速插入内容的油猴脚本

不过 AutoHotkey 这些功能都有,算是重复造轮子了

代码如下:

    // ==UserScript==
// @name         多功能快捷插入助手 (Ultimate)
// @namespace    http://tampermonkey.net/
// @version      5.0
// @description  支持无限添加自定义快捷键与符号 (React/ 推特 /Notion 全兼容)
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // === 1. 初始化配置 (自动迁移旧数据) ===
    const defaultRules = [
        {
            id: 'default_1',
            symbol: "▸",
            shortcut: {key: ".", ctrl: true, alt: false, shift: false, meta: false}
        }
    ];

    // 获取数据,如果格式是旧版的对象,强转为数组
    let rules = GM_getValue("userRules", defaultRules);
    if (!Array.isArray(rules)) {
        // 尝试迁移 v4.0 的旧数据
        const oldConfig = GM_getValue("userConfig");
        if (oldConfig) {
            rules = [{id: 'migrated_' + Date.now(),
                symbol: oldConfig.symbol || "▸",
                shortcut: oldConfig.shortcut || defaultRules[0].shortcut
            }];
        } else {rules = defaultRules;}
        GM_setValue("userRules", rules);
    }

    // 注册菜单
    GM_registerMenuCommand("⚙️ 管理快捷键规则 ", showSettingsModal);

    // === 2. 核心监听逻辑 ===
    document.addEventListener('keydown', function(e) {
        // 如果在设置面板内,不触发插入
        if (document.getElementById('tm-ultimate-settings-modal')) return;

        // 忽略纯修饰键
        if (['Control', 'Alt', 'Shift', 'Meta'].includes(e.key)) return;

        // 遍历所有规则,寻找匹配项
        const matchedRule = rules.find(rule => {
            const s = rule.shortcut;
            return (e.key.toLowerCase() === s.key.toLowerCase()) &&
                   (e.ctrlKey === s.ctrl) &&
                   (e.altKey === s.alt) &&
                   (e.shiftKey === s.shift) &&
                   (e.metaKey === s.meta);
        });

        if (matchedRule) {
            const el = document.activeElement;
            if (!el) return;

            // Shadow DOM 穿透
            let target = el;
            if (el.shadowRoot && el.shadowRoot.activeElement) {target = el.shadowRoot.activeElement;}

            const isInput = target.tagName === 'INPUT' || target.tagName === 'TEXTAREA';
            const isContentEditable = target.isContentEditable;

            if (isInput || isContentEditable) {e.preventDefault();
                e.stopPropagation();

                // 处理动态占位符(比如当前日期)const textToInsert = processDynamicText(matchedRule.symbol);
                insertText(target, textToInsert);
            }
        }
    }, true);

    // 处理动态文本 (可选功能)
    function processDynamicText(text) {const now = new Date();
        return text
            .replace('{{date}}', now.toLocaleDateString())
            .replace('{{time}}', now.toLocaleTimeString());
    }

    // 插入逻辑 (保持 React 兼容性)
    function insertText(el, text) {
        let success = false;
        try {success = document.execCommand('insertText', false, text);
        } catch (err) {}

        if (!success && (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA')) {insertToReactInput(el, text);
        }
    }

    function insertToReactInput(input, text) {
        const start = input.selectionStart;
        const end = input.selectionEnd;
        const originalValue = input.value;
        const newValue = originalValue.substring(0, start) + text + originalValue.substring(end);
        const nativeSetter = Object.getOwnPropertyDescriptor(window[`HTML${input.tagName === 'TEXTAREA' ? 'TextArea' : 'Input'}Element`].prototype, "value").set;
        nativeSetter && nativeSetter.call(input, newValue);
        input.dispatchEvent(new Event('input', { bubbles: true}));
        const newPos = start + text.length;
        input.setSelectionRange(newPos, newPos);
    }

    // === 3. 增强版设置 UI ===
    function showSettingsModal() {if (document.getElementById('tm-ultimate-settings-modal')) return;

        // 样式注入
        const style = document.createElement('style');
        style.innerHTML = `
            #tm-ultimate-settings-modal * {box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;}
            .tm-row {display: flex; align-items: center; gap: 10px; margin-bottom: 10px; padding: 10px; background: #f8f9fa; border-radius: 6px; border: 1px solid #eee;}
            .tm-input {flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px;}
            .tm-shortcut-btn {flex: 0 0 140px; padding: 8px; border: 1px solid #ddd; border-radius: 4px; background: #fff; cursor: pointer; text-align: center; font-size: 13px; color: #555;}
            .tm-shortcut-btn.recording {border-color: #1da1f2; color: #1da1f2; background: #e8f5fe;}
            .tm-del-btn {color: #dc3545; cursor: pointer; padding: 5px 10px; font-size: 18px; line-height: 1; opacity: 0.6;}
            .tm-del-btn:hover {opacity: 1;}
            .tm-add-btn {width: 100%; padding: 10px; background: #eef3f8; color: #1da1f2; border: 2px dashed #bwd; border-radius: 6px; cursor: pointer; font-weight: bold; margin-bottom: 15px; text-align: center;}
            .tm-add-btn:hover {background: #e1eef9;}
        `;
        document.head.appendChild(style);

        const modal = document.createElement('div');
        modal.id = 'tm-ultimate-settings-modal';
        modal.innerHTML = `
            <div style="position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.4);z-index:999999;display:flex;justify-content:center;align-items:center;">
                <div style="background:white;padding:25px;border-radius:12px;width:550px;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 10px 30px rgba(0,0,0,0.2);">
                    <h3 style="margin:0 0 20px 0;font-size:18px;color:#333;display:flex;justify-content:space-between;">
                        <span>🎹 快捷键管理 </span>
                        <span style="font-size:12px;color:#888;font-weight:normal;line-height:24px;"> 支持 {{date}} 占位符 </span>
                    </h3>

                    <div id="tm-rules-list" style="overflow-y:auto;flex:1;padding-right:5px;margin-bottom:15px;">
                        </div>

                    <div id="tm-add-btn" class="tm-add-btn">+ 添加新规则 </div>

                    <div style="text-align:right;border-top:1px solid #eee;padding-top:15px;">
                        <button id="tm-cancel-btn" style="padding:8px 20px;margin-right:10px;border:none;background:#f0f0f0;cursor:pointer;border-radius:6px;"> 取消 </button>
                        <button id="tm-save-btn" style="padding:8px 20px;border:none;background:#1da1f2;color:white;cursor:pointer;border-radius:6px;font-weight:bold;"> 保存生效 </button>
                    </div>
                </div>
            </div>
        `;
        document.body.appendChild(modal);

        // 临时数据副本
        let tempRules = JSON.parse(JSON.stringify(rules));
        const listContainer = modal.querySelector('#tm-rules-list');

        // 渲染列表函数
        function render() {
            listContainer.innerHTML = '';
            tempRules.forEach((rule, index) => {const row = document.createElement('div');
                row.className = 'tm-row';
                row.innerHTML = `
                    <input type="text" class="tm-input symbol-input" placeholder=" 输入要插入的文字 " value="${rule.symbol}">
                    <div class="tm-shortcut-btn" tabindex="0">${formatShortcut(rule.shortcut)}</div>
                    <div class="tm-del-btn" title=" 删除 ">×</div>
                `;

                // 绑定事件
                // 1. 修改符号
                row.querySelector('.symbol-input').oninput = (e) => {rule.symbol = e.target.value;};

                // 2. 录制快捷键
                const shortcutBtn = row.querySelector('.tm-shortcut-btn');
                shortcutBtn.onclick = () => {
                    // 重置其他正在录制的按钮
                    document.querySelectorAll('.tm-shortcut-btn').forEach(b => {b.classList.remove('recording');
                        if(b !== shortcutBtn) b.innerText = formatShortcut(tempRules[findRuleIndexByBtn(b)].shortcut);
                    });

                    shortcutBtn.classList.add('recording');
                    shortcutBtn.innerText = " 请按下按键...";
                };

                shortcutBtn.onkeydown = (e) => {if (!shortcutBtn.classList.contains('recording')) return;
                    e.preventDefault();
                    e.stopPropagation();
                    if (['Control', 'Alt', 'Shift', 'Meta'].includes(e.key)) return;

                    rule.shortcut = {
                        key: e.key,
                        ctrl: e.ctrlKey,
                        alt: e.altKey,
                        shift: e.shiftKey,
                        meta: e.metaKey
                    };
                    shortcutBtn.classList.remove('recording');
                    shortcutBtn.innerText = formatShortcut(rule.shortcut);
                    shortcutBtn.blur();};

                // 3. 删除
                row.querySelector('.tm-del-btn').onclick = () => {tempRules.splice(index, 1);
                    render();};

                listContainer.appendChild(row);
            });
        }

        // 辅助:找对应的 Rule Index
        function findRuleIndexByBtn(btn) {const rows = Array.from(listContainer.children);
            return rows.indexOf(btn.parentElement);
        }

        render();

        // 按钮事件
        modal.querySelector('#tm-add-btn').onclick = () => {
            tempRules.push({id: Date.now().toString(),
                symbol: "",
                shortcut: {key: "", ctrl: false, alt: false, shift: false, meta: false} // 空规则
            });
            render();
            // 自动滚动到底部
            setTimeout(() => listContainer.scrollTop = listContainer.scrollHeight, 50);
        };

        modal.querySelector('#tm-cancel-btn').onclick = () => {document.body.removeChild(modal);
            document.head.removeChild(style);
        };

        modal.querySelector('#tm-save-btn').onclick = () => {
            // 简单验证:过滤掉没有快捷键的规则
            rules = tempRules.filter(r => r.shortcut.key);
            GM_setValue("userRules", rules);
            document.body.removeChild(modal);
            document.head.removeChild(style);
            // 提示
            const msg = document.createElement('div');
            msg.innerHTML = ` 已保存 ${rules.length} 条规则 `;
            msg.style.cssText = "position:fixed;bottom:20px;right:20px;background:#333;color:#fff;padding:10px 20px;border-radius:4px;z-index:999999;animation:fadeout 2s forwards;";
            document.body.appendChild(msg);
            setTimeout(() => msg.remove(), 2000);
        };
    }

    function formatShortcut(s) {if (!s || !s.key) return " 点击录制快捷键 ";
        const parts = [];
        if (s.ctrl) parts.push("Ctrl");
        if (s.meta) parts.push("Cmd");
        if (s.alt) parts.push("Alt");
        if (s.shift) parts.push("Shift");
        let k = s.key;
        if (k === ' ') k = 'Space';
        parts.push(k.toUpperCase());
        return parts.join(" + ");
    }

})();

🤝 创作不易,感谢支持

您的支持是我持续输出的动力

🟡 币安 Binance 返 20%注册 →
邀请码: SRXT5KUM合约: 416378774
⚫ 欧易 OKX 返 20%注册 →
邀请码: A999998
📚 Web3 教程 阅读
✈️ 电报交流群 加入
Web3 钱包福利
🔵 币安 (返 30%)

邀请码 RP3AEJ2M 绑定领取
⚫ OKX (返 20%)

邀请码 1234567 绑定领取
加密资产波动大 • 能力要求高 • 请自行判断

正文完
 0
柴郡
版权声明:本站原创文章,由 柴郡 于2025-12-18发表,共计6781字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)